#include <kernel/gdt.h>
#include <kernel/pcb.h>
#include <video.h>
#include <kernel/registros.h>
#include <kernel/idle.h>
#include <kernel/sched.h>
#include <kernel/syscall.h>
#include <kernel/libk/string.h>
#include <kernel/shm.h>

#include <fs/ramfs.h>

#include <kernel/mem/mem_part_kernel.h>

#define ASM_ARG( iArgumento, uiBaseSegmento )		\
	__asm__ volatile ( 				\
		"push %%eax \n\t" 		\
		"push %%ebx \n\t" 		\
		"movl	%0, %%eax \n\t" 		\
		"movl %1, %%ebx \n\t" 		\
		"mov %%ebx, (%%eax) \n\t" 		\
		"pop %%ebx \n\t" 		\
		"pop %%eax \n\t" 		\
		:"=g"( uiBaseSegmento ), 				\
		"=g"( iArgumento ) )


extern dword pdwGDT;
extern stuPCB pstuPCB[CANTMAXPROCS];
extern stuTSS stuTSSTablaTareas[CANTMAXPROCS];



iCantEnEspera = 0;

unsigned int uiUltimoPid = 0;
unsigned long ulProcActual = 0;
unsigned long ulUltimoProcesoEnFPU = 0; //PID del ultimo proceso que uso el FPU

unsigned char ucLecturaTemporalELF[TAM_ELF]; // Variable donde copio la cabecera ELF

unsigned int uiBaseEnviromentPaginacion = 0xA0100;

// 2010 - Grupo 1 - Variables para copiar de la cabecera ELF el Program Header y el Section Header
unsigned char ucLecturaTemporalPHeaederELF[TAM_PHT_ELF];
unsigned char ucLecturaTemporalSHeaederELF[TAM_SHT_ELF];

#define TOTAL_ENTRADAS_GDT (sizeof(stuEstructuraGdt) / sizeof(stuGDTDescriptor))

unsigned char iMapaGDT[ TOTAL_ENTRADAS_GDT / 8 ];


unsigned char iMapaSegmentos[ TOTAL_SEGMENTOS / 8 ];

#define _SET_BIT( bitmap, pos, set ) 					\
	do{								\
		if( set ){						\
			bitmap[ (pos-(pos%8)) / 8 ] |= (1 << (7-((pos)%8))); \
		} else	{						\
			bitmap[ (pos-(pos%8)) / 8 ] &= ~(1 << (7-((pos)%8))); \
		}							\
	}while( 0 )

#define _GET_BIT( bitmap, pos ) 					\
			(bitmap[ (pos) / 8 ] & (1 << (7-((pos)%8)))) 	\

#define mapa_gdt_set( pos, set ) _SET_BIT( iMapaGDT, pos, set )
#define mapa_gdt_get( pos ) _GET_BIT( iMapaGDT, pos )

#define mapa_segmentos_set( pos, set ) _SET_BIT( iMapaSegmentos, pos, set )
#define mapa_segmentos_get( pos ) _GET_BIT( iMapaSegmentos, pos )

#define EN_GRANULARIDAD_4K( x ) ( ((x) + 0x0FFF) >> 12 )
#define EN_GRANULARIDAD_1B( x ) ( (x) << 12 )

#define REDONDEAR_HACIA_ARRIBA_A_4K( x ) \
                ( EN_GRANULARIDAD_1B( EN_GRANULARIDAD_4K (x) ) )


/**
 * @brief Inicializa la GDT y la tabla de PCBs
 * @param Puntero a la GDT
 */
void
vFnGdtInicializar (dword pdwGDT)
{
  int iN;
  pstuTablaGdt = (stuEstructuraGdt *) pdwGDT;


  for( iN = 0; iN < sizeof( iMapaGDT ) / sizeof( iMapaGDT[0]); iN++ )
	  iMapaGDT[iN] = 0; // bloque de entradas de la GDT no utilizadas

  mapa_gdt_set( 0, 1 ); /* descriptor nulo */
  mapa_gdt_set( 1, 1 ); /* descriptor codigo del kernel */
  mapa_gdt_set( 2, 1 ); /* descriptor datos+stack del kernel */
  mapa_gdt_set( 3, 1 ); // descriptor APM_32_CS
  mapa_gdt_set( 4, 1 ); // descriptor APM_16_CS
  mapa_gdt_set( 5, 1 ); // descriptor APM_DATA
  mapa_gdt_set( 6, 1 ); // callgate APM (Ring 0)
  mapa_gdt_set( 7, 1 ); /* descriptor CallGate Para el SysCaller */



  for (iN = 0; iN < CANTMAXPROCS; iN++)
    {
      pstuPCB[iN].iEstado = PROC_NO_DEFINIDO;
    }
/*  for( iN = 0; iN < sizeof( iMapaSegmentos ) / sizeof(iMapaSegmentos[0]); iN++ )
	  iMapaSegmentos[iN] = 0; // bloque de segmentos no utilizados

  for( iN = 0; iN < 32; iN++) // 16; iN++ )
	  mapa_segmentos_set( iN, 1 );	// los primeros 32 segmentos (4Mb) los dejamos para el kernel
*/
}


/**
 * @brief Devuelve la posicion en el vector de procesos del pid indicado
 * @param PID del proceso
 * @returns Posición en el vector de procesos o -1 si no existe
 * @date 04/08/2008
 */
int iFnBuscaPosicionProc (unsigned long ulPid) {
    int iN = 0;

    while (iN < CANTMAXPROCS) {
        if( (pstuPCB[iN].ulId == ulPid) &&
            (pstuPCB[iN].iEstado != PROC_NO_DEFINIDO) &&
            (pstuPCB[iN].iEstado != PROC_ELIMINADO) ) {
                return iN;
        }
        iN++;
    }

    return -1;
}

/**
\brief Carga en el descriptor de la GDT indicado la direccion de memoria base y el limite (sin especificar granularidad)
\param uiPosicion Posicion en la GDT del descriptor a modificar
\param uiBase Direccion base (lineal)
\param uiLimite Longitud (afectado por la granularidad)
\returns posicion en la GDT del descriptor
\note Tener en cuenta que, el largo del segmento descripto sera ( (uiLimite+1) * GR). Por ejemplo, con granularidad 4Kb un uiLimite = 0 determina un largo de segmento descripto de ( (0+1) * 4Kb) = 4Kb.
*/
unsigned int
uiFnSetearBaseLimiteDescriptor( int uiPosicion,
				unsigned int uiBase,
				unsigned int uiLimite )
{
	pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].usBaseBajo = uiBase & 0xFFFF;
	pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].ucBaseMedio = (uiBase >> 16) & 0xFF;
	pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].usBaseAlto = uiBase >> 24;
	pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].usLimiteBajo = uiLimite & 0xFFFF;
	pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].bitLimiteAlto = (uiLimite >> 16) & 0x0F;

	return uiPosicion;
}

/**
 * @brief Agrega un descriptor en la GDT
 * @param uiBase Direccion inicial del segmento en memoria
 * @param uiLimite Longitud del segmento (afectado por la granularidad)
 * @param uiOpt (tipo de descriptor, acceso, crecimiento, granularidad, etc)
 * @returns Offset desde la base de la GDT
 * @date 09/04/2006
 */

unsigned int
uiFnAgregarDescriptorGDT (unsigned int uiBase, unsigned int uiLimite,
			  unsigned int uiOpt, int uiPosicion)
{
  uiFnSetearBaseLimiteDescriptor( uiPosicion, uiBase, uiLimite );

  pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].ucAcesso = (uiOpt + D_PRESENT) >> 8;
  pstuTablaGdt->stuGdtDescriptorDescs[uiPosicion].bitGranularidad = ((uiOpt & 0xff) >> 4);

  return (uiPosicion);
}



/**
 * @brief Busca una posicion libre en el vector de procesos
 * @returns La posicion libre en el vector de procesos o -1 si no hay posiciones
 * libres
 * @date 04/08/2008
 */
int iFnBuscarPCBLibre()
{
  int iPosicion = 0;

    while ( iPosicion < CANTMAXPROCS ) {
        if (pstuPCB[iPosicion].iEstado == PROC_ELIMINADO ||
            pstuPCB[iPosicion].iEstado == PROC_NO_DEFINIDO ) {
                return iPosicion;
        }
        iPosicion++;
    }

    return -1;
}


/**
 * @brief Busca una posicion libre en la GDT (mapa)
 * @returns La posicion libre en la GDT o 0 si no hay posicion libre
 * @date 04/08/2008
 */
unsigned int uiFnBuscarEntradaGDTLibre()
{
  unsigned int uiPosicion = 0;

    while ( uiPosicion < TOTAL_ENTRADAS_GDT ) {
        if ( !mapa_gdt_get( uiPosicion ) ) {
            mapa_gdt_set( uiPosicion, 1 ); // marcamos como usada
            return uiPosicion;
        }
        uiPosicion++;
    }
  return 0;
}

/**
 * @brief Busca un segmento libre en el mapa de segmentos
 * @returns La posicion libre o 0 si no hay posicion libre
 * @date 04/08/2008
 */
unsigned int uiFnBuscarSegmentoLibre()
{
  unsigned int uiPosicion = 0;

  while (uiPosicion < TOTAL_SEGMENTOS && mapa_segmentos_get( uiPosicion ) )
    {
      uiPosicion++;
    }

  if (uiPosicion < TOTAL_SEGMENTOS ){
	  mapa_segmentos_set( uiPosicion, 1 ); // marcamos como usada
	  return uiPosicion;
  }

  return 0;
}

/**
\brief Crea una TSS para una nueva TAREA_ESPECIAL (llamamos TAREA_ESPECIAL a los procesos 'simulados' por Sodium (Shell, Reloj y los lanzados con exec), aquellos que tienen su stack dentro de la TSS en espacio0, espacio1, etc).
\param pEIP puntero al codigo de la función que queremos ejecutar
\param iPosicion posicion dentro de la GDT
\param uiCS direccion del CS
\param uiDS direccion del DS
\param uiSS direccion del SS
\returns posicion en la tabla de procesos
\sa iFnCrearTSS
\date 02/10/2008
*/
int
iFnCrearTSSReloj (void *pEIP,
        int iPosicion,
        unsigned int uiCS,
        unsigned int uiDS,
        unsigned int uiSS)
{
    int iN = 0;

    stuTSSTablaTareas[iPosicion].trapbit = 0;
    stuTSSTablaTareas[iPosicion].uIOMapeoBase = sizeof (stuTSS);
    stuTSSTablaTareas[iPosicion].cs = uiCS;

    stuTSSTablaTareas[iPosicion].fs = uiDS;
    stuTSSTablaTareas[iPosicion].gs = uiDS;
    stuTSSTablaTareas[iPosicion].ds = uiDS;
    stuTSSTablaTareas[iPosicion].es = uiDS;
    stuTSSTablaTareas[iPosicion].ss = uiSS;

    stuTSSTablaTareas[iPosicion].ss0 = uiSS;
    stuTSSTablaTareas[iPosicion].ss1 = uiSS;
    stuTSSTablaTareas[iPosicion].ss2 = uiSS;

    stuTSSTablaTareas[iPosicion].esp0 =
        (unsigned int) &stuTSSTablaTareas[iPosicion].
        espacio0[TSS_TAMANIO_STACK_SS0 - 8];

    stuTSSTablaTareas[iPosicion].esp1 =
        (unsigned int) &stuTSSTablaTareas[iPosicion].
        espacio1[TSS_TAMANIO_STACK_R1 - 8];

    stuTSSTablaTareas[iPosicion].esp2 =
        (unsigned int) &stuTSSTablaTareas[iPosicion].
        espacio2[TSS_TAMANIO_STACK_R2 - 8];

    stuTSSTablaTareas[iPosicion].esp =
        (unsigned int) &stuTSSTablaTareas[iPosicion].
        espacio0[TSS_TAMANIO_STACK_SS0 - 8];

    stuTSSTablaTareas[iPosicion].ebp =
        (unsigned int) &stuTSSTablaTareas[iPosicion].
        espacio0[TSS_TAMANIO_STACK_SS0 - 8];

    //Rellenamos los stacks de los distintos rings de ejecucion con patrones
    //faciles de distinguir. Facilita mucho el debugueo del contenido del stack
    //en tiempo de ejecucion con los comandos "stack" y "dump"
    for (iN = 0; iN < TSS_TAMANIO_STACK_SS0; iN++) {
        stuTSSTablaTareas[iPosicion].espacio0[iN] = 0x11L;
    }
    for (iN = 0; iN < TSS_TAMANIO_STACK_R1; iN++) {
        stuTSSTablaTareas[iPosicion].espacio1[iN] = 0x22L;
    }
    for (iN = 0; iN < TSS_TAMANIO_STACK_R2; iN++) {
        stuTSSTablaTareas[iPosicion].espacio2[iN] = 0x33L;
    }

    //0x3202L; para tareas ring3
    stuTSSTablaTareas[iPosicion].uiEBandera = 0x202L;

    stuTSSTablaTareas[iPosicion].eip = (unsigned int) pEIP;
    stuTSSTablaTareas[iPosicion].ldt = 0;
    stuTSSTablaTareas[iPosicion].eax = 0;
    stuTSSTablaTareas[iPosicion].ebx = 0;
    stuTSSTablaTareas[iPosicion].ecx = 0;
    stuTSSTablaTareas[iPosicion].edx = 0;

    /* Valores para FPU (17-07-08) */
    //Redondeo al mas cercano, Doble precision, Interrupcion por
    //Precision enmascarada (bit PM),
    //el resto de las interrupciones sin enmascarar y Stack vacio.
    stuTSSTablaTareas[iPosicion].fpu.control = 0x0360;
    stuTSSTablaTareas[iPosicion].fpu.status = 0x0000;
    stuTSSTablaTareas[iPosicion].fpu.tag = 0xFFFF;
    /*    stuTSSTablaTareas[iPosicion].fpu.ip = 0;
    stuTSSTablaTareas[iPosicion].fpu.cs = 0;
    stuTSSTablaTareas[iPosicion].fpu.dp = 0;
    stuTSSTablaTareas[iPosicion].fpu.ds = 0;*/
    /* FIN Valores para FPU */

	stuTSSTablaTareas[iPosicion].cr3 = BASEDIRECTORIO;
    return (iPosicion);
}



/**
\brief Crea una TSS para un nuevo proceso NORMAL (ver iFnCrearTSSReloj)
\param pEIP Puntero al codigo de la funcion que queremos ejecutar
\param pESP Puntero al stack del proceso
\param iPosicion Posicion dentro de la GDT
\param uiStackOffset Usado para saber que tamaño tienen el enviroment + argc + argv
\param uiCS Direccion del CS
\param uiDS Direccion del DS
\param uiSS Direccion del SS
\param iPrivilegio Nivel de privilegio pasado por parametro
\returns Posicion en la tabla de TSSs
\sa iFnCrearTSSReloj
\date 02/10/2008
*/
int iFnCrearTSS (void *pEIP,
	      void *pESP,
	     int iPosicion,
	     unsigned int uiStackOffset,
	     unsigned int uiCS,
	     unsigned int uiDS,
	     unsigned int uiSS,
	     unsigned int uiSS0,
         int iPrivilegio,
		 unsigned int uiCr3)
{
        if (iPrivilegio == 3)
          {
           uiCS |= 0x3;
           uiDS |= 0x3;
	   uiSS |= 0x3;
          }
        if (iPrivilegio == 2)
          {
           uiCS |= 0x2;
           uiDS |= 0x2;
	   uiSS |= 0x2;
          }
        if (iPrivilegio == 1)
          {
           uiCS |= 0x1;
           uiDS |= 0x1;
	   uiSS |= 0x1;
          }

	stuTSSTablaTareas[iPosicion].trapbit = 0;
	stuTSSTablaTareas[iPosicion].uIOMapeoBase = sizeof (stuTSS);
	stuTSSTablaTareas[iPosicion].cs = uiCS;
	stuTSSTablaTareas[iPosicion].fs = uiDS;
	stuTSSTablaTareas[iPosicion].gs = uiDS;
	stuTSSTablaTareas[iPosicion].ds = uiDS;
	stuTSSTablaTareas[iPosicion].es = uiDS;
	stuTSSTablaTareas[iPosicion].ss = uiSS;

        /* Los siguientes stacks sirven para tasks switch, cuando un task de nivel 0 hace un ljump a
           una tarea de otro nivel, utiliza el ss0 seteado en la tarea destino. La de nivel 1 utiliza la
           ss1. Como el hecho de que una tarea nivel 3 pueda hacer un task switch sería contradictorio
           a la seguridad (razon misma por la que se usan stacks de diferentes niveles) no hay un ss3.
           Si algun task switch llega a fallar desde una aplicacion a nivel 1 o 2, podría estar mal seteado
           el selector de stack  */
    stuTSSTablaTareas[iPosicion].ss0 = uiSS0; //(uiSS & 0xFC);
	stuTSSTablaTareas[iPosicion].ss1 = 0x21; // Stack de Kernel a nivel 1, esto es absolutamente necesario.
	stuTSSTablaTareas[iPosicion].ss2 = 0x2A; // Stack de Kernel a nivel 2, esto es absolutamente necesario.

    stuTSSTablaTareas[iPosicion].esp0 = (void*)(TSS_TAMANIO_STACK_SS0 - 0x1);
    stuTSSTablaTareas[iPosicion].esp1 = (void*)(TSS_TAMANIO_STACK_SS0 - 0x1);
    stuTSSTablaTareas[iPosicion].esp2 = (void*)(TSS_TAMANIO_STACK_SS0 - 0x1);

	stuTSSTablaTareas[iPosicion].esp = (unsigned int) (pESP - uiStackOffset);
	stuTSSTablaTareas[iPosicion].ebp = (unsigned int) (pESP - uiStackOffset);

	stuTSSTablaTareas[iPosicion].uiEBandera = 0x3202L;	//0x3202L; para tareas ring3

	stuTSSTablaTareas[iPosicion].eip = (unsigned int) pEIP;
	stuTSSTablaTareas[iPosicion].ldt = 0;
	stuTSSTablaTareas[iPosicion].eax = 0;
	stuTSSTablaTareas[iPosicion].ebx = 0;
	stuTSSTablaTareas[iPosicion].ecx = 0;
	stuTSSTablaTareas[iPosicion].edx = 0;

	/* Valores para FPU (17-07-08) */
	//Redondeo al mas cercano, Doble precision, Interrupcion por Precision enmascarada (bit PM),
    //el resto de las interrupciones sin enmascarar y Stack vacio.
	stuTSSTablaTareas[iPosicion].fpu.control = 0x0360;
	stuTSSTablaTareas[iPosicion].fpu.status = 0x0000;
	stuTSSTablaTareas[iPosicion].fpu.tag = 0xFFFF;
	/*	stuTSSTablaTareas[iPosicion].fpu.ip = 0;
	stuTSSTablaTareas[iPosicion].fpu.cs = 0;
	stuTSSTablaTareas[iPosicion].fpu.dp = 0;
	stuTSSTablaTareas[iPosicion].fpu.ds = 0;*/
	/* FIN Valores para FPU */
	
	stuTSSTablaTareas[iPosicion].cr3 = uiCr3;
	return (iPosicion);
}


/**
\brief Crea un PCB
\param iPosicion Posicion dentro de la tabla de PCBs
\param pEIP Puntero al inicio del codigo
\param stNombre Nombre del proceso
\param uiIndiceGDT_CS Posicion del descriptor del segmento de codigo en la GDT
\param uiIndiceGDT_DS Posicion del descriptor del segmento de datos en la GDT
\param uiIndiceGDT_TSS Posicion del descriptor de la TSS en la GDT
\param uiPosTSS Posicion de la TSS en la tabla de TSS (idem iPosicion)
\param uiDirBase Direccion base de memoria (absoluta)
\param uiLimite Longitud de memoria (aqui NO existe granularidad)
\returns iPosicion
*/
int iFnCrearPCB( int iPosicion,
         void *pEIP,
         char *stNombre,
         unsigned int uiIndiceGDT_CS,
         unsigned int uiIndiceGDT_DS,
         unsigned int uiIndiceGDT_TSS,
         unsigned int uiPosTSS,
         unsigned int uiDirBase,
         unsigned int uiLimite,
         unsigned int uiTamanioTexto,
         unsigned int uiTamanioDatosInicializados,
         unsigned int uiTamanioStack,
         unsigned int uiBaseSs0,
         unsigned int uiIndiceGDT_SS0)
{
	int iN;
	int iJ;
	tms tmsDefault;
	itimerval itimervalDefault;

	tmsDefault.tms_utime = 0;
	tmsDefault.tms_stime = 0;
	tmsDefault.tms_cutime = 0;
	tmsDefault.tms_cstime = 0;
	itimervalDefault.it_interval.tv_sec = 0;
	itimervalDefault.it_interval.tv_usec = 0;
	itimervalDefault.it_value.tv_sec = 0;
	itimervalDefault.it_value.tv_usec = 0;

	pstuPCB[iPosicion].uiIndiceGDT_CS = uiIndiceGDT_CS;
	pstuPCB[iPosicion].uiIndiceGDT_DS = uiIndiceGDT_DS;
	pstuPCB[iPosicion].uiIndiceGDT_TSS = uiIndiceGDT_TSS;

	pstuPCB[iPosicion].vFnFuncion 	= pEIP;
	pstuPCB[iPosicion].iEstado 	= PROC_LISTO;	//Lista para ejecucion
	pstuPCB[iPosicion].ulLugarTSS 	= uiPosTSS;
	pstuPCB[iPosicion].ulId 	= uiUltimoPid++;
	pstuPCB[iPosicion].ulParentId 	= pstuPCB[ulProcActual].ulId;
	pstuPCB[iPosicion].uiDirBase 	= uiDirBase;
	pstuPCB[iPosicion].uiLimite 	= uiLimite;
	pstuPCB[iPosicion].stuTmsTiemposProceso	= tmsDefault;
	pstuPCB[iPosicion].timers[0]=itimervalDefault;
	pstuPCB[iPosicion].timers[1]=itimervalDefault;
	pstuPCB[iPosicion].timers[2]=itimervalDefault;
	pstuPCB[iPosicion].lNanosleep 	= 0;
	pstuPCB[iPosicion].puRestoDelNanosleep 	= NULL;

	pstuPCB[iPosicion].uiBaseSs0 = uiBaseSs0;
    pstuPCB[iPosicion].uiIndiceGDT_SS0 = uiIndiceGDT_SS0;

    //TODO - Revisar cambio:
    //pstuPCB[iPosicion].uiTamProc    = 0;
    pstuPCB[iPosicion].uiTamProc    = uiLimite;
	pstuPCB[iPosicion].lPidTracer 	= PROC_WOTRACER;

	for (iN = 0; iN < 12; iN++)
	  {
	    pstuPCB[iPosicion].stNombre[iN] = stNombre[iN];
	  }

	pstuPCB[iPosicion].stNombre[iN] = '\0';

	/*agregado por el grupo*/
	pstuPCB[iPosicion].iPrioridad = 0;

	pstuPCB[iPosicion].uiLimite 	= uiLimite;

	//inicializo el vector de memorias comartidas
	for (iJ = 0; iJ < MAXSHMEMPORPROCESO; iJ ++)
	{
		pstuPCB[iPosicion].memoriasAtachadas[iJ].utilizada = FALSE;
	}
    //Agregado 2008 - GRUPO PArticion
    pstuPCB[iPosicion].uiTamanioTexto = uiTamanioTexto;
    pstuPCB[iPosicion].uiTamanioDatosInicializados=uiTamanioDatosInicializados;
    pstuPCB[iPosicion].uiTamanioStack = uiTamanioStack;
    //Fin Agregado 2008

	return iPosicion;
}


/**
\brief Intancia al proceso Reloj que forma parte del kernel en nivel 0. Es el unico proceso
       que no se crea a partir de un archivo.
\param puntero al codigo de la funcion que queremos ejecutar
\param nombre de la tarea
\returns posicion en la tabla de procesos
*/
int iFnInstanciarReloj( void *pEIP, char *stNombre )
{
    unsigned short int bInterrupcionesHabilitadas = 0;
    unsigned long int *puliParametroStack;
    unsigned int uiIndiceGDT_TSS;
    int iPosicion;
    int iFlags;

    __asm__ ("pushf\n pop %%eax": "=a" (iFlags):);
    // si estaban habilitadas, aqui se deshabilitan
    if (iFlags & 0x200){
        __asm__ ("cli"::);
        bInterrupcionesHabilitadas = 1;
    }

    //Se crean todas las estructuras necesarias
    iPosicion = iFnBuscarPCBLibre();
    uiIndiceGDT_TSS = uiFnBuscarEntradaGDTLibre();

    //Si no hay entradas libres suficientes en la PCB (y TSS) o en la GDT,
    //liberamos los recursos tomados (las entradas en la GDT)
    if( iPosicion == -1 || !uiIndiceGDT_TSS ) {
            if(uiIndiceGDT_TSS) { mapa_gdt_set( uiIndiceGDT_TSS, 0 ); }
            return -EAGAIN;
    }


    /* TSS */

    if( iPosicion != iFnCrearTSSReloj( pEIP,
                                                iPosicion,
                                                wFnGetCS(),
                                                wFnGetDS(),
                                                wFnGetSS() ) ) {
        /* XXX
         * Si llegamos aqui es porque no se pudo crear la TSS del proceso init.
         * Hoy en día iFnCrearTSS siempre retorna iPosicion, por lo que nunca
         * se alacanza este punto.
         */
    }

    // Se 'agrega' un descriptor en la GDT para que apunte a la nueva TSS
    uiFnAgregarDescriptorGDT (
            (unsigned int)&stuTSSTablaTareas[iPosicion],//Dir base del segmento
            sizeof( stuTSS ),                           //Longitud del segmento
            (D_TSS | D_BIG),                            //Opciones
            uiIndiceGDT_TSS);                           //Posicion en la GDT

    /* PCB */

    iFnCrearPCB( iPosicion,     /* PCB asignada */
             pEIP,              /* funcion a ejecutar */
             stNombre,          /* nombre del proceso */
             1,                 /* posicion del CS del kernel */
             2,                 /* posicion del DS del kernel */
             uiIndiceGDT_TSS,   /* indice del descript de la TSS en la GDT */
             iPosicion,         /* posicion de la TSS en la tabla de TSSs
                                   (igual a la de la tabla de PCBs) */
             0x000000,          /* base del segmento (usa el del kernel, por lo
                                   tanto, es cero) */
             0xffffffff,        /* limite fisico (4gb) */
             0,                 /* Tamanio del codigo*/
             0,                 /* Tamanio del bloque de datos inicializados*/
             0,                  /* Tamanio del Stack*/
             0,
             0
             );

    //TODO: Revisar
    pstuPCB[iPosicion].uiTamanioOverhead = 0;

    //ESCRIBIMOS EN EL STACK SPACE (RING 0) DEL PROCESO CREADO EL CONTENIDO DE
    //LA VARIABLE PID QUE DICHO PROCESO TOMA COMO PARAMETRO
    puliParametroStack =
        (unsigned long int *)
        &( stuTSSTablaTareas[iPosicion].espacio0[TSS_TAMANIO_STACK_SS0 - 4] );
    *puliParametroStack = (unsigned long int) pstuPCB[iPosicion].ulId;

    if (bInterrupcionesHabilitadas)
      __asm__ ("sti"::);

    return iPosicion;
}


/**
 * @brief Copia 'size' bytes de 'orig' a 'dest'
 *
 * XXX Optimizar reescribiendo en inline assembler
 */
unsigned char* ucpFnMemCopy( unsigned char *ucpDestino, unsigned char *ucpOrigen, unsigned int uiTamanio )
{
	unsigned char *ucpDestinoOriginal = ucpDestino;

	while( uiTamanio-- )
		*ucpDestino++ = *ucpOrigen++;

	return ucpDestinoOriginal;
}

/**
 * @brief Instancia el proceso Idle
 * @returns La posicion dentro de la Tabla de PCBs
 */
int iFnInstanciarIdle()
{
    iTareaNula = iFnCrearProceso("IDLE.BIN",0,0);
    return iTareaNula;
}

/**
\brief Crea una CallGate
\param iPosicion Posicion en la GDT que va a ocupar la callgate
\param usOffsetBajo Ver estructura de una callgate y equivalencias con gdt de codigo/datos
\returns Si hubo exito, devuelve la posicion en la GDT de la DPL
*/
int iFnCrearCallGate(int iPosicion, unsigned short usOffsetBajo, unsigned short usSegmentSelector, unsigned char ucParameterCount, unsigned int uiTipo, unsigned int uiAcceso, unsigned short usOffsetAlto)
{
    unsigned short int bInterrupcionesHabilitadas = 0;
    int iFlags;

    /* Antes que nada hay que buscar las correspondencias entre GDT "normal" a callgate
    unsigned short usOffsetBajo: Declaraciones para traducir de gdt de datos a callgate (corregir eso)
    unsigned short usSegmentSelector
    unsigned char ucParameterCount: Solo los primeros 4 bits, el resto 0 para CallGate
    unsigned int uiTipo: 1100
    unsigned int uiAcceso: 1XX0  donde XX es el DPL
    unsigned short usOffsetAlto:
     */


    __asm__ ("pushf\n pop %%eax": "=a" (iFlags):);
    // Si estaban habilitadas, aqui se deshabilitan
    if (iFlags & 0x200){
        __asm__ ("cli"::);
        bInterrupcionesHabilitadas = 1;
    }

    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].usLimiteBajo = usOffsetBajo;
    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].usBaseBajo = usSegmentSelector;
    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].ucBaseMedio = ucParameterCount;
    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].ucAcesso |= uiTipo;
    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].ucAcesso |= uiAcceso;
    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].bitGranularidad = 0x0; //usOffsetAlto;
    pstuTablaGdt->stuGdtDescriptorDescs[iPosicion].usBaseAlto = 0x0; // usOffsetAlto;

    if (bInterrupcionesHabilitadas)
      __asm__ ("sti"::);

    return 1;
}


/**
\brief Agrega una entrada a la lista de binarios esperando por ser ejecutados
\param stArchivo Nombre del archivo binario con el cual crear el proceso
\returns Si hubo exito, la posicion dentro de la Tabla de PCBs, si no, un numero menor a 0
*/
void vFnAgregarListaEspera(char* stBinario, long int liTamanio)
{
   iFnCopiaCadena(stListaEspera[iCantEnEspera].stBinario,stBinario);
   stListaEspera[iCantEnEspera].liTamanio = liTamanio;
   iCantEnEspera++;
}

/**
\brief Muestralista de binarios esperando por ser ejecutados
\returns Si hubo exito, la posicion dentro de la Tabla de PCBs, si no, un numero menor a 0
*/
void vFnRecorrerListaEspera()
{
   int i;

   vFnImprimir("\nLista de programas en espera");
   vFnImprimir("\n----------------------------");
   vFnImprimir("\n\nNombre Binario                Tamanio (Bytes)");

   for(i = 0; i < iCantEnEspera; i++) vFnImprimir("\n   %s                %d",stListaEspera[i].stBinario,stListaEspera[i].liTamanio);
}



/**
\brief Crea un proceso a partir de un archivo binario
\param stArchivo Nombre del archivo binario con el cual crear el proceso
\returns Si hubo exito, la posicion dentro de la Tabla de PCBs, si no, un numero menor a 0
*/
int iFnCrearProceso( char* stArchivo, int iPrivilegio, int iArgumento)
{
    unsigned short int bInterrupcionesHabilitadas = 0;
    unsigned int uiIndiceGDT_CS, uiIndiceGDT_DS, uiIndiceGDT_TSS, uiIndiceGDT_SS0; //Kultenko - adding uiIndiceGDT_SS0
    unsigned int uiBaseSegmento, uiBaseSS0=0; //Kultenko - adding uiBaseSS0

    int iPosicion;
    int iFlags;
	int iSize; // Tamaño del nombre del ejecutable
    int i;
	int iN;
	
    unsigned int uiTamSegCodigo;
    unsigned int uiTamSegDatos;
    unsigned int uiTamStack;
	unsigned int uiPosStack;
	unsigned int uiTamSS0;
	unsigned int uiEnviromentSize; // Sergio Martin 2010 - Indica tamaño de la cadena del enviroment
    unsigned int uiOffset; // Sergio Martin 2010 - Indica tamaño del enviroment + argv + argc
    unsigned int uiTamHeap; // Sergio Martin 2009 - Indica tamaño del Heap
    unsigned int uiTamInicializados;
    unsigned int uiOpt;
	unsigned int uiTamanioTotalProceso; // Grupo N° 1 2010 - Tamaño del proceso
	unsigned int uiCR3;
	
    char* pstEnviroment; // Sergio Martin 2010 - Puntero al enviroment leido.
    char  stConfig[9];
	int*  piVectorEnviroment;

    stuInfoEjecutable stuInfoExe;


    __asm__ ("pushf\n pop %%eax": "=a" (iFlags):);
    // Si estaban habilitadas, aqui se deshabilitan
    if (iFlags & 0x200){
        __asm__ ("cli"::);
        bInterrupcionesHabilitadas = 1;
    }

    //Se lee la cabecera del archivo binario
    if( iFnLeerCabeceraEjecutable(stArchivo, &stuInfoExe) == -1) {
        return -1;
    }

    //Vamos a crear descriptores con granularidad 4K, por ello convertimos los
    //datos leidos del binario a granularidad 4K
    uiTamSegCodigo     = REDONDEAR_HACIA_ARRIBA_A_4K(stuInfoExe.uiTamanioTexto);
    uiTamStack         = REDONDEAR_HACIA_ARRIBA_A_4K(stuInfoExe.uiTamanioStack);
	uiTamSS0           = REDONDEAR_HACIA_ARRIBA_A_4K(TSS_TAMANIO_STACK_SS0);
    uiTamInicializados = REDONDEAR_HACIA_ARRIBA_A_4K(stuInfoExe.uiTamanioDatosInicializados);
    uiTamHeap          = 100000; // Es el tamaño por defecto para el primer proceso INIT que crea al shell.
    uiTamSegDatos      = REDONDEAR_HACIA_ARRIBA_A_4K(uiTamSegCodigo + uiTamStack + uiTamInicializados + uiTamHeap+ uiTamSS0); //Se crea CON heap
	uiPosStack	       = POS_STACK; // uiTamSegCodigo + uiTamInicializados + uiTamStack;
	uiBaseSS0	       = uiTamSegDatos;
	
    //Se crean todas las estructuras necesarias
    iPosicion = iFnBuscarPCBLibre(); //Es la misma posicion para la TSS
    uiIndiceGDT_CS = uiFnBuscarEntradaGDTLibre();
    uiIndiceGDT_DS = uiFnBuscarEntradaGDTLibre();
    uiIndiceGDT_TSS = uiFnBuscarEntradaGDTLibre();
    uiIndiceGDT_SS0 = uiFnBuscarEntradaGDTLibre();// Kultenko - adding new variable
	
	// Grupo N° 1 2010 - Asignamos paginas al proceso recien creado
	
	if (usMemPaginadaHabilitada == MODOPAGINADO)
	{
		// Crear los datos administrativos para el proceso (DPT, MPT);
		uiCR3 = (unsigned int)(uiFnAsignarMemoriaProceso(pstuPCB[iPosicion].ulId, uiTamSegDatos, iPrivilegio));
		vFnImprimir("CR3: 0x%x\n", uiCR3);
		
		vFnCopiarProcesoMemoria(uiCR3,stuInfoExe.pvPuntoCarga,uiTamSegCodigo);
		
		unsigned int stupInicioTablaPagina = ((stuTablaDirectorio *)(uiCR3))->stupEntradasDirectorio[1].uiDirBase * uiTamanioPagina;
		
		BX_MAGIC_BREAKPOINT;
		uiBaseSegmento = ((stuTablaPaginas * )stupInicioTablaPagina)->stupEntradasTablaPaginas[0].uiDirBase *uiTamanioPagina;  
		vFnImprimir("\nuiBaseSegmento: %x - %x - %x",uiBaseSegmento,((stuTablaPaginas * )stupInicioTablaPagina)->stupEntradasTablaPaginas[0].uiDirBase *uiTamanioPagina, ((stuTablaDirectorio *)(uiCR3))->stupEntradasDirectorio[1].uiDirBase  *uiTamanioPagina);
		BX_MAGIC_BREAKPOINT;
	}
	else
	{
		//Se reserva la memoria para el proceso (se usa un unico segmento para Codigo y Datos)
		uiBaseSegmento = (unsigned int) pvFnReservarParticionUsuario( uiTamSegDatos );
		if( uiBaseSegmento == -1 ) {
			vFnImprimir("\nNo hay suficiente espacio para la asignacion,\n agregando a la lista de procesos en espera...");
			vFnAgregarListaEspera(stArchivo,stArchivo);
			return -ENOMEM;
		}

		uiBaseSS0 = (unsigned int) pvFnReservarParticionUsuario( TSS_TAMANIO_STACK_SS0 );
		if( uiBaseSS0 == -1 ) {
			vFnImprimir("\nNo hay suficiente espacio para la asignacion del stack,\n agregando a la lista de procesos en espera...");
			vFnKFree( (void*)uiBaseSegmento );
			vFnAgregarListaEspera(stArchivo,stArchivo);
			return -ENOMEM;
		}

	}

    //Si no hay entradas libres suficientes en la PCB (y TSS) o en la GDT,
    //liberamos los recursos tomados (la memoria y las entradas en la GDT)
    if( iPosicion == -1 ||
        !uiIndiceGDT_CS ||!uiIndiceGDT_DS || !uiIndiceGDT_TSS ) {
			
			vFnKFree( (void*)uiBaseSegmento );
            vFnKFree( (void*)uiBaseSS0 );
            if(uiIndiceGDT_SS0) { mapa_gdt_set( uiIndiceGDT_SS0, 0 ); } //Kultenko - ss0 segment
            if(uiIndiceGDT_CS ) { mapa_gdt_set( uiIndiceGDT_CS,  0 ); }
            if(uiIndiceGDT_DS ) { mapa_gdt_set( uiIndiceGDT_DS,  0 ); }
            if(uiIndiceGDT_TSS) { mapa_gdt_set( uiIndiceGDT_TSS, 0 ); }
            return -EAGAIN;
    }


    /* Descriptor de CODIGO */
	
    uiOpt = (D_CODE | D_READ | D_BIG | D_4KB); // Seteo las opciones basado
    if (iPrivilegio == 3) uiOpt |= D_DPL3;     // en el nivel de privilegio
    if (iPrivilegio == 2) uiOpt |= D_DPL2;     // indicado por parametro
    if (iPrivilegio == 1) uiOpt |= D_DPL1;

    uiFnAgregarDescriptorGDT (
            uiBaseSegmento,                         //Dir base del segmento
            EN_GRANULARIDAD_4K(uiTamSegCodigo) - 1, //Longitud del segmento
            uiOpt,      //Opciones
            uiIndiceGDT_CS);                        //Posicion en la GDT


    /* Descriptor de DATOS */
	
    uiOpt = (D_DATA | D_WRITE | D_BIG | D_4KB);
    if (iPrivilegio == 3) uiOpt |= D_DPL3;    // Seteo las opciones basado
    if (iPrivilegio == 2) uiOpt |= D_DPL2;    // en el nivel de privilegio
    if (iPrivilegio == 1) uiOpt |= D_DPL1;    // indicado por parametro

    uiFnAgregarDescriptorGDT (
            uiBaseSegmento,                         //Dir base del segmento
            EN_GRANULARIDAD_4K(uiTamSegDatos) - 1,  //Longitud del segmento
            uiOpt,     //Opciones
            uiIndiceGDT_DS);                        //Posicion en la GDT

	
    //Kultenko - adding new descriptor for stack ss0
    uiFnAgregarDescriptorGDT (
	uiBaseSS0,                  //Dir base del segmento
	TSS_TAMANIO_STACK_SS0 - 1,  //Longitud del segmento
	(D_DATA | D_WRITE | D_BIG | D_4KB ),
	uiIndiceGDT_SS0);


    /* COPIA de CODIGO */
	
	if (usMemPaginadaHabilitada == MODOPAGINADO)
	{
		vFnLog("\nCopiando el codigo del proceso \"%s\" de 0x%x a 0x%x, ocupa %d "
				"bytes", stuInfoExe.stNombre, stuInfoExe.pvPuntoCarga,
				uiBaseSegmento, stuInfoExe.uiTamanioTexto);
	}
	else
	{
		vFnLog("\nCopiando el codigo del proceso \"%s\" de 0x%x a 0x%x, ocupa %d "
				"bytes", stuInfoExe.stNombre, stuInfoExe.pvPuntoCarga,
				uiBaseSegmento, stuInfoExe.uiTamanioTexto);
		ucpFnCopiarMemoria(
			(unsigned char*)uiBaseSegmento,
			(unsigned char*)stuInfoExe.pvPuntoCarga,
			stuInfoExe.uiTamanioTexto );
	}
    /////////////////  Comienzo de seteo de enviroment, y argc / argv.///////////////////////////
    //  Antes de crear la TSS, debemos calcular el offset de comienzo  //////////////////////////
    //  de EBP y ESP respecto al final del segmento de datos           //////////////////////////
    //  donde se guardaran argc, argv y el enviroment.                 //////////////////////////
    //  Sergio Martin - 2010                                           //////////////////////////
    /////////////////////////////////////////////////////////////////////////////////////////////

	if (usMemPaginadaHabilitada != MODOPAGINADO)
	{
	   pstEnviroment = pstFnObtenerEnviroment();   	   
		   
	   
	   uiEnviromentSize = 0; // Ultimo caracter = '\0' 
	   while(pstEnviroment[uiEnviromentSize] != '\0') 
	     { 
		   if(pstEnviroment[uiEnviromentSize] == '\n')pstEnviroment[uiEnviromentSize] = '\0'; 
		   uiEnviromentSize++;
	     }
	   
	   uiOffset = uiEnviromentSize;
	   
	   // Copiamos el enviroment antes del comienzo del stack
	   
       for(i=0; i < uiOffset; i++) 
	   {
			*(unsigned char*)(uiBaseSegmento + uiPosStack - uiOffset + i ) = pstEnviroment[i];
	   }
	
	   // Seteamos los punteros a cada palabra de adelante hacia atras
       // Primero el último puntero nulo
	    uiOffset = uiOffset + sizeof(int);
		*(int*)(uiBaseSegmento + uiPosStack - uiOffset ) = 0 ;

		
          for( i = uiEnviromentSize -1 ; i > 0; i--) // el -1 es porque no queremos tener en cuenta el '\0' final ppd.
		    {
			   if(pstEnviroment[i] == '\0')
			     {
				    uiOffset = uiOffset + sizeof(int);
					*(int*)(uiBaseSegmento + uiPosStack - uiOffset ) = uiPosStack - uiEnviromentSize + i +1 ; // El +1 es porque queremos el Inicio de la cadena, No el '\0'
				 }
			}
		  		  
		  
		  uiOffset = uiOffset + sizeof(int);
	      piVectorEnviroment = uiPosStack - uiOffset;
		  *(int*)(uiBaseSegmento + uiPosStack - uiOffset) = uiPosStack - uiEnviromentSize;
		  uiOffset = uiOffset + 2*sizeof(int);
		  
	  // Para este funcion, ejecutar se genera con argc y argv sin parametros
	  // Recordando que CrearProceso no es la forma estandar de instanciar un proceso (Fork + Execve)
	  // y solo se conserva para instanciar al shell (por ahora, deberia usarse solo para el init)
	  // Se conserva como opcion para crear un nuevo proceso solo por fines pedagogicos
	  // Y solo pasa el enviroment por defecto y un solo parametro: el nombre del binario sin el .BIN
	  
	  // **Argv
		  // Nombre del binario
		  iSize = 0;
		  while(stuInfoExe.stNombre[iSize] != '\0') iSize++;
		  iSize = iSize - 3; // le saco el BIN
		  uiOffset = uiOffset + iSize;
		  for(i=0; i < iSize; i++) 
			*(unsigned char*)(uiBaseSegmento + uiPosStack - uiOffset + i + sizeof(int) ) = stuInfoExe.stNombre[i];
			
		  *(unsigned char*)(uiBaseSegmento + uiPosStack - uiOffset + iSize + sizeof(int) ) = '\0';
		  
		  // Ultimo puntero nulo del vector de punteros
		  uiOffset = uiOffset + sizeof(int);
		  *(int*)(uiBaseSegmento + uiPosStack - uiOffset + sizeof(int) ) = 0;
		  // Puntero al nombre
		  uiOffset = uiOffset + sizeof(int);
		  *(int*)(uiBaseSegmento + uiPosStack - uiOffset + sizeof(int) ) = uiPosStack - uiOffset + 3*sizeof(int);
		 
         // Seccion Final: Dobles punteros: *argv[], *envp[], y argc.

		 // creamos el doble puntero al enviroment
		  uiOffset = uiOffset + sizeof(int);
		  *(int*)(uiBaseSegmento + uiPosStack - uiOffset + sizeof(int) ) = piVectorEnviroment;
		  // Puntero al vector de punteros
		  uiOffset = uiOffset + sizeof(int);
		  *(int*)(uiBaseSegmento + uiPosStack - uiOffset + sizeof(int) ) = uiPosStack - uiOffset + 3*sizeof(int); 
	      // Argc
		  uiOffset = uiOffset + sizeof(int);
		  *(int*)(uiBaseSegmento + uiPosStack - uiOffset + sizeof(int) ) = 1;

  	    if(usMemPaginadaHabilitada != MODOPAGINADO)
		{
			vFnKFree(pstEnviroment); // Liberamos el puntero del enviroment.
		}
	}
	// */
	/////////////////////////////////////////////////////////////////////////////////////////////////
	//////////////// Fin de seteo de enviroment, y argc / argv //////////////////////////////////////
	/////////////////////////////////////////////////////////////////////////////////////////////////

    /* TSS */
    if( iPosicion != iFnCrearTSS( 0x0, /* arranca en la pos 0 del binario */
                                    (void*)POS_STACK,
                                    iPosicion,
                                    uiOffset,
                                    uiIndiceGDT_CS * 8,
                                    uiIndiceGDT_DS * 8,
                                    uiIndiceGDT_DS * 8,
                                    uiIndiceGDT_SS0 * 8,
                                    iPrivilegio, 
									uiCR3 ) ) {
        /* XXX
         * Si llegamos aqui es porque no se pudo crear la TSS del proceso init.
         * Hoy en día iFnCrearTSS siempre retorna iPosicion, por lo que nunca
         * se alacanza este punto.
         */
    }

    // Se 'agrega' un descriptor en la GDT para que apunte a la nueva TSS
    uiFnAgregarDescriptorGDT (
            (unsigned int)&stuTSSTablaTareas[iPosicion],//Dir base del segmento
            sizeof( stuTSS ),                           //Longitud del segmento
            (D_TSS | D_BIG),                            //Opciones
            uiIndiceGDT_TSS);                           //Posicion en la GDT

    /* PCB */
    iFnCrearPCB( iPosicion,     /* PCB de init */
             0x0,               /* direccion de arranque de init */
             stuInfoExe.stNombre,   /* nombre */
             uiIndiceGDT_CS,    /* Posicion del descrip CODIGO en la GDT */
             uiIndiceGDT_DS,    /* Posicion del descrip DATOS  en la GDT */
             uiIndiceGDT_TSS,   /* Posicion del descrip TSS    en la GDT */
             iPosicion,         /* posicion dentro de la tabla de TSSs */
             uiBaseSegmento,    /* dir base del segmento asignado a este proc */
             uiTamSegDatos,     /* LONGITUD del segmento */
             uiTamSegCodigo,    /* Tamanio del codigo */
             uiTamInicializados,/* Tamanio del bloque de datos inicializados */
             uiTamStack,         /* Tamanio del Stack */
             uiBaseSS0,         // Base del stack ss0
             uiIndiceGDT_SS0    // Posicion del descrip SS0 en la GDT - Sergio Martin 2010
             );
    
	
    // Ahora copio la cabecera ELF al PCB.
    // vFnLog("\nDireccion: 0x%x",(int)&pstuPCB[iPosicion].stuCabeceraELF);
    ucpFnCopiarMemoria((unsigned char*)&pstuPCB[iPosicion].stuCabeceraELF,(unsigned char*)ucLecturaTemporalELF, TAM_ELF);
	
	ucpFnCopiarMemoria((unsigned char*)&pstuPCB[iPosicion].stSHeaderData,(unsigned char*)ucLecturaTemporalSHeaederELF, 0xF0);

    // Este es el Tamaño del codigo bien extraìdo del ELF
    // vFnLog("\nTamanio BIN: %d",pstuPCB[iPosicion].stuCabeceraELF.PHeaderData[0].memsz);

    //TODO: Revisar
    /* La memoria 'extra' asignada al segmento de datos se puede reutilizar
     * cuando crece el stack, en cambio lo que se asigna de mas al segmento de
     * codigo no se reutiliza.
     */
    pstuPCB[iPosicion].uiTamanioOverhead =
                                    uiTamSegCodigo - stuInfoExe.uiTamanioTexto;
    pstuPCB[iPosicion].uiEsperaTeclado = 0;



     // Pasar el parametro directamente al data segment del proceso a lo Rambo.
 /*    if(iArgumento)
    {
       uiBaseSegmentoDatos += (uiTamSegDatos - 0x10) + 0x04;
       vFnLog("\nParametro: %x",uiBaseSegmentoDatos);
       ASM_ARG( iArgumento, uiBaseSegmentoDatos );
     }*/
	 
    if (bInterrupcionesHabilitadas)
      __asm__ ("sti"::);

    return iPosicion;
}

/**
\fn char* pstFnObtenerEnviroment( )
\brief Carga el enviroment en el puntero recibido
\return El tamaño del enviroment en bytes
\date 06/03/2010
*/
char* pstFnObtenerEnviroment()
{
	unsigned int uiSize;
	unsigned int i;
	char *pstEnviroment;

	// Ahora definimos el enviroment provisoriamente
	// Pero debe ser leido de un archivo al estilo UNIX
	// utilizando un archivo

	char stEnviromentProvisorio[]="\
PorcentajePartFija=10\n\
TamanioHeapUsuario=100000\n\
TipoPartFija=FIJO\n\
ModoMemUser=VARIABLE\n\
ModoShell=PASIVO\n\
OutputProceso=NORMAL";  // TOP



	// Primero contamos la cantidad de bytes crudos

	uiSize = 0;
	while (stEnviromentProvisorio[uiSize] != '\0')  uiSize++;

	if (usMemPaginadaHabilitada == MODOPAGINADO)
	{
		pstEnviroment = (char*)uiBaseEnviromentPaginacion;
		uiBaseEnviromentPaginacion += uiSize*sizeof(char);
	}
	else
		pstEnviroment = (char*)pvFnReservarParticionKernel(uiSize*sizeof(char));
	
	for(i = 0; i <= uiSize; i++)  pstEnviroment[i] = stEnviromentProvisorio[i];

	return pstEnviroment;
}




/**
 * @brief Redimensiona un Proceso (cambia tamanio y posicion de segmento de datos, posicion de segmento de codigo y actualiza las estructuras asodiadas (GDT, PCB, TSS, etc) )
 * @param uiPid Pid del proceso a realocar
 * @param ulBrk Nuevo tamanio para el proceso
 * @returns Ejecucion correcta: 0. Error: -1
 */
int iFnRedimensionarProceso(unsigned long ulPid, unsigned long ulBrk) {
    unsigned long ulDirBaseNueva;
    stuPCB* pPCB;

    pPCB = &pstuPCB[ iFnBuscaPosicionProc(ulPid) ];

    //Convertimos la direccion de BRK en multiplo de 4Kb, ya que usamos los
    //descriptores para los procesos con granularidad 4Kb
    ulBrk = REDONDEAR_HACIA_ARRIBA_A_4K(ulBrk);


    /* Comprobamos que el nuevo ulBrk no sea inferior al minimo permitido para
     * este proceso (menor al area de codigo, datos y stack), ni sea menor que
     * la direccion maxima de las memorias compartidas que tenga en este momento
     */
    if( ulBrk <= (pPCB->uiTamanioTexto +
                  pPCB->uiTamanioDatosInicializados +
                  pPCB->uiTamanioStack) ||
        ulBrk <= ulFnMaxDirShmProc(ulPid) ) {
        vFnLog("\niFnRedimensionarProceso: No se puede redimensionar proceso, "
                "BRK invalido (valor muy bajo)");
        return -1;
    }

    /* Comprobamos tambien que el nuevo ulBrk no supere el maximo de memoria
     * permitida para un proceso
     */
    if( ulBrk >= MAX_MEMORIA_PROCESO) {
        vFnLog("\niFnRedimensionarProceso: No se puede redimensionar proceso, "
                "BRK invalido (valor mayor al permitido por el sistema)");
        return -1;
    }


    ulDirBaseNueva = (unsigned long) pvFnRedimensionarSegmento( (void *)pPCB->uiDirBase, ulBrk);

    //Si no se pudo hacer el redimensionamiento, se deja el segmento como estaba
    //y devolvemos -1
    if(ulDirBaseNueva < 0) {
        vFnLog("\niFnRedimensionarProceso: No se pudo redimensionar proceso");
        return -1;
    }

    //Actualizamos el PCB
    pPCB->uiDirBase = ulDirBaseNueva;
    pPCB->uiLimite  = ulBrk;
    pPCB->uiTamProc = ulBrk;

    //Actualizamos los descriptores de la GDT (granularidad 4Kb)
    uiFnSetearBaseLimiteDescriptor(pPCB->uiIndiceGDT_CS, ulDirBaseNueva,
            EN_GRANULARIDAD_4K(ulBrk) - 1 );
    uiFnSetearBaseLimiteDescriptor(pPCB->uiIndiceGDT_DS, ulDirBaseNueva,
            EN_GRANULARIDAD_4K(ulBrk) - 1 );

    vFnLog("\niFnRedimensionarProceso: Se redimensiono Proceso PID=%d", ulPid);

    return 0;
}


/**
 * @brief Duplica un Proceso
 * @param uiProcPadre La posicion dentro de la Tabla de PCBs del proceso padre
 * @returns La posicion dentro de la Tabla de PCBs del proceso creado
 */
int iFnDuplicarProceso( unsigned int uiProcPadre ){
	unsigned int uiIndiceGDT_CS,
		     uiIndiceGDT_DS,
		     uiIndiceGDT_TSS,
		     uiIndiceGDT_SS0, //Kultenko - adding uiIndiceGDT_SS0
		     uiStackUsuario,
		     uiComienzoStackKernel,
		     uiBaseSegmento,
		     uiBaseSS0=0; //Kultenko - adding uiBaseSS0;

    unsigned int uiTamSegCodigo;
    unsigned int uiTamSegDatos;
    unsigned int uiTamStack;
    unsigned int uiTamInicializados;
    unsigned int uiOpt;

    unsigned int *puiPuntero;

    uiTamSegCodigo = pstuPCB[uiProcPadre].uiTamanioTexto;
    //uiTamSegDatos = pstuPCB[uiProcPadre].uiLimite;
    uiTamHeap          = uiTamHeap; // Definido por el shell previo a hacer fork
    uiTamSegDatos = REDONDEAR_HACIA_ARRIBA_A_4K(pstuPCB[uiProcPadre].uiTamanioTexto+pstuPCB[uiProcPadre].uiTamanioStack+pstuPCB[uiProcPadre].uiTamanioDatosInicializados + uiTamHeap);
    uiTamStack = pstuPCB[uiProcPadre].uiTamanioStack;
    uiTamInicializados = pstuPCB[uiProcPadre].uiTamanioDatosInicializados;

	int iPosicion = 0;

    /* No deshabilitamos las interrupciones porque esta funcion se invoca desde
     * el syscall fork(), que a su vez es llamado por el handler de la int 0x80,
     * por lo cual al momento de comenzar la atencion de la interrupcion, se
     * deshabilitan las demas
     */
     vFnLog("\nComenzando FORK");

	iPosicion = iFnBuscarPCBLibre();
	//uiBaseSegmento = uiFnBuscarSegmentoLibre() * SEGMENT_SIZE;
uiBaseSegmento = (unsigned int) pvFnReservarParticionUsuario( uiTamSegDatos );
    if( uiBaseSegmento == -1 ) {
        vFnLog("\nfork(): No hay particion libre suficiente para fork.");
        return -1;
    }
   vFnLog("\nBase segmento: %d",uiBaseSegmento);

    uiBaseSS0 = (unsigned int) pvFnReservarParticionUsuario( TSS_TAMANIO_STACK_SS0 );
    if( uiBaseSS0 == -1 ) {
        vFnImprimir("\nNo hay suficiente espacio para la asignacion del stack,\n agregando a la lista de procesos en espera...");
        vFnKFree( (void*)uiBaseSegmento );
        return -1;
    }


    //Se crean todas las estructuras necesarias
	uiIndiceGDT_CS = uiFnBuscarEntradaGDTLibre();
	uiIndiceGDT_DS = uiFnBuscarEntradaGDTLibre();
	uiIndiceGDT_TSS = uiFnBuscarEntradaGDTLibre();
	uiIndiceGDT_SS0 = uiFnBuscarEntradaGDTLibre();// Kultenko - adding new variable

    //Si no hay entradas libres suficientes en la PCB (y TSS) o en la GDT,
    //liberamos los recursos tomados (la memoria y las entradas en la GDT)
	// XXX habria que desmarcar el segmento o las entradas en la GDT que SI se pudieron asignar
    if( iPosicion == -1 ||
        !uiIndiceGDT_CS ||!uiIndiceGDT_DS || !uiIndiceGDT_TSS ) {
            vFnKFree( (void*)uiBaseSegmento );
            if(uiIndiceGDT_SS0) { mapa_gdt_set( uiIndiceGDT_SS0, 0 ); }
            if(uiIndiceGDT_CS ) { mapa_gdt_set( uiIndiceGDT_CS,  0 ); }
            if(uiIndiceGDT_DS ) { mapa_gdt_set( uiIndiceGDT_DS,  0 ); }
            if(uiIndiceGDT_TSS) { mapa_gdt_set( uiIndiceGDT_TSS, 0 ); }
            return -EAGAIN;
    }
#if 1
	vFnLog( "\n fork(): iFnBuscarPCBLibre: %d", iPosicion );
    vFnLog( "\n fork(): Inicio nuevo segmento: 0x%x", uiBaseSegmento );
     vFnLog( "\n fork(): uiFnBuscarEntradaGDTLibre(SS0): %d, 0x%x",
            uiIndiceGDT_SS0, uiIndiceGDT_SS0 * 8 );
    vFnLog( "\n fork(): uiFnBuscarEntradaGDTLibre(CS): %d, 0x%x",
            uiIndiceGDT_CS, uiIndiceGDT_CS * 8 );
    vFnLog( "\n fork(): uiFnBuscarEntradaGDTLibre(DS): %d, 0x%x",
            uiIndiceGDT_DS, uiIndiceGDT_DS * 8 );
    vFnLog( "\n fork(): uiFnBuscarEntradaGDTLibre(TSS): %d, 0x%x",
            uiIndiceGDT_TSS, uiIndiceGDT_TSS * 8 );
#endif

    /* Descriptor de CODIGO */

	ucpFnCopiarMemoria( (unsigned char*) &pstuTablaGdt->stuGdtDescriptorDescs[uiIndiceGDT_CS],
		(unsigned char*) &pstuTablaGdt->stuGdtDescriptorDescs[stuTSSTablaTareas[uiProcPadre].cs / 8],
		sizeof( stuGDTDescriptor ) );
	// divido por 4k porque me estoy copiando el selector de datos del kernel, que tiene
	// granularidad en 4k



    /* Descriptor de CODIGO */

    uiOpt = (D_CODE | D_READ | D_BIG | D_4KB | D_DPL3); // Seteo las opciones basado

    uiFnAgregarDescriptorGDT (
            uiBaseSegmento,                         //Dir base del segmento
            EN_GRANULARIDAD_4K(uiTamSegCodigo) - 1, //Longitud del segmento
            uiOpt,      //Opciones
            uiIndiceGDT_CS);                        //Posicion en la GDT

  /*  uiFnSetearBaseLimiteDescriptor( uiIndiceGDT_CS,
                                    uiBaseSegmento,
                                    EN_GRANULARIDAD_4K( uiTamSegCodigo ) - 1 ); */


	ucpFnCopiarMemoria( (unsigned char*) &pstuTablaGdt->stuGdtDescriptorDescs[uiIndiceGDT_DS],
		(unsigned char*) &pstuTablaGdt->stuGdtDescriptorDescs[stuTSSTablaTareas[uiProcPadre].ds / 8],
		sizeof( stuGDTDescriptor ) );

    /* Descriptor de DATOS */

    uiOpt = (D_DATA | D_WRITE | D_BIG | D_4KB | D_DPL3);

    uiFnAgregarDescriptorGDT (
            uiBaseSegmento,                         //Dir base del segmento
            EN_GRANULARIDAD_4K(uiTamSegDatos) - 1,  //Longitud del segmento
            uiOpt,     //Opciones
            uiIndiceGDT_DS);                        //Posicion en la GDT


	/* Descriptor de Stack Segment nivel 0 */
	ucpFnCopiarMemoria( (unsigned char*) &pstuTablaGdt->stuGdtDescriptorDescs[uiIndiceGDT_SS0],
		(unsigned char*) &pstuTablaGdt->stuGdtDescriptorDescs[stuTSSTablaTareas[uiProcPadre].ss0 / 8],
		sizeof( stuGDTDescriptor ) );

    //Kultenko - adding new descriptor for stack ss0
    uiFnAgregarDescriptorGDT (
	uiBaseSS0,                  //Dir base del segmento
	EN_GRANULARIDAD_4K(TSS_TAMANIO_STACK_SS0 - 1),  //Longitud del segmento
	(D_DATA | D_WRITE | D_BIG | D_4KB ),
	uiIndiceGDT_SS0);



	/* copio todo el segmento del proc actual al nuevo */
	ucpFnCopiarMemoria( (unsigned char*)uiBaseSegmento,
		(unsigned char*)pstuPCB[uiProcPadre].uiDirBase,
                        uiTamSegDatos );

   /* copio todo el segmento del ss0 del proc actual al nuevo */
	ucpFnCopiarMemoria( (unsigned char*)uiBaseSS0,
		(unsigned char*)pstuPCB[uiProcPadre].uiBaseSs0,
                        TSS_TAMANIO_STACK_SS0 );





    /* TSS */

	/* armamos la TSS del proceso hijo */
 	stuTSSTablaTareas[iPosicion].trapbit = 0;
	stuTSSTablaTareas[iPosicion].uIOMapeoBase = sizeof (stuTSS);
    // Le asignamos los selectores de segmento que encontramos con
    // uiFnBuscarEntradaGDTLibre()

     stuTSSTablaTareas[iPosicion].cs = (uiIndiceGDT_CS * 8) | 0x03 ;
     stuTSSTablaTareas[iPosicion].ds = (uiIndiceGDT_DS * 8) | 0x03;
	 stuTSSTablaTareas[iPosicion].ss = (uiIndiceGDT_DS * 8) | 0x03;
	 stuTSSTablaTareas[iPosicion].es = (uiIndiceGDT_DS * 8) | 0x03;
	 stuTSSTablaTareas[iPosicion].fs = (uiIndiceGDT_DS * 8) | 0x03 ;
	 stuTSSTablaTareas[iPosicion].gs = (uiIndiceGDT_DS * 8) | 0x03;
	 stuTSSTablaTareas[iPosicion].ss0 = (uiIndiceGDT_SS0 * 8);
  	 stuTSSTablaTareas[iPosicion].ss1 = 0;
	 stuTSSTablaTareas[iPosicion].ss2 = 0;

 	stuTSSTablaTareas[iPosicion].esp0 = (unsigned int) (TSS_TAMANIO_STACK_SS0 - 0x1);
	stuTSSTablaTareas[iPosicion].esp1 = (unsigned int) (TSS_TAMANIO_STACK_SS0 - 0x1);
	stuTSSTablaTareas[iPosicion].esp2 = (unsigned int) (TSS_TAMANIO_STACK_SS0 - 0x1);

	/* ahora comenzamos a copiar los valores de los registros, sacandolos de lo
	 * que se fue apilando al momento de comenzar el manejo de la syscall */
	/* el comienzo del stack del kernel se calcula en **system_asm.asm**,
	 * cualquier cambio ahi (que implique desplazar el stack), debe contemplarse
	 * aqui ya que este calculo resultaria invalido */
	uiComienzoStackKernel = uiKernelStack - 0x08 - 0x100 * ((pstuPCB[uiProcPadre].uiIndiceGDT_TSS * 8)-0x20);
	stuTSSTablaTareas[iPosicion].esp = *(unsigned int*)(uiComienzoStackKernel-0x08);
	uiStackUsuario = stuTSSTablaTareas[iPosicion].esp + pstuPCB[uiProcPadre].uiBaseSs0;

     //  vFnImprimir("\nComienzo Kernel: 0x%x", uiComienzoStackKernel);
      // vFnImprimir("\nESP: 0x%x",stuTSSTablaTareas[iPosicion].esp);
     //  vFnImprimir("\nStackUsuario: 0x%x",uiStackUsuario);

	stuTSSTablaTareas[iPosicion].ebp = *(unsigned int*)(uiComienzoStackKernel-0x0c);
	stuTSSTablaTareas[iPosicion].edx = *(unsigned int*)(uiComienzoStackKernel-0x10);
	stuTSSTablaTareas[iPosicion].ecx = *(unsigned int*)(uiComienzoStackKernel-0x14);
 	stuTSSTablaTareas[iPosicion].ebx = *(unsigned int*)(uiComienzoStackKernel-0x18);
	stuTSSTablaTareas[iPosicion].eax = 0;//0x00; // al hijo se le devuelve cero!!
#if 0
        vFnImprimir("\nEBP Original: 0x%x",stuTSSTablaTareas[iPosicion].ebp);
        vFnImprimir("\nEDX Original: 0x%x",stuTSSTablaTareas[iPosicion].edx);
        vFnImprimir("\nECX Original: 0x%x",stuTSSTablaTareas[iPosicion].ecx);
        vFnImprimir("\nEBX Original: 0x%x",stuTSSTablaTareas[iPosicion].ebx);
        vFnImprimir("\nEAX Original: 0x%x",stuTSSTablaTareas[iPosicion].eax);
#endif


	/* calculamos la posicion del fin stack del usuario, tomando la base del segmento
	 * del proceso (de su PCB) y el valor de ESP (apilado en el stack del kernel) */
	//  stuTSSTablaTareas[iPosicion].esp = *(unsigned int*)(uiStackUsuario+0x0c);
	  stuTSSTablaTareas[iPosicion].edi = *(unsigned int*)(uiStackUsuario+0x04);
	  stuTSSTablaTareas[iPosicion].esi = *(unsigned int*)(uiStackUsuario+0x08);
	  stuTSSTablaTareas[iPosicion].eip = *(unsigned int*)(uiStackUsuario+0x1C);

	   stuTSSTablaTareas[iPosicion].uiEBandera = 0x3202L;
	 //  stuTSSTablaTareas[iPosicion].esp = *(unsigned int*)(uiStackUsuario+0x0C);
#if 0
        vFnImprimir("\nEIP: 0x%x",stuTSSTablaTareas[iPosicion].eip);
        vFnImprimir("\nESP: 0x%x",stuTSSTablaTareas[iPosicion].esp);
        vFnImprimir("\nEDI: 0x%x",stuTSSTablaTareas[iPosicion].edi);
        vFnImprimir("\nESI: 0x%x",stuTSSTablaTareas[iPosicion].esi);
        vFnImprimir("\nEBP: 0x%x",stuTSSTablaTareas[iPosicion].ebp);
        vFnImprimir("\nFLAGS: 0x%x",stuTSSTablaTareas[iPosicion].uiEBandera);
 #endif
//while(1);

	stuTSSTablaTareas[iPosicion].ldt = stuTSSTablaTareas[uiProcPadre].ldt;



	uiFnAgregarDescriptorGDT ((unsigned int)
                    &stuTSSTablaTareas[iPosicion], sizeof( stuTSS ),
                    (D_TSS + D_BIG), uiIndiceGDT_TSS);



#if 0
	vFnImprimir( "\n fork(): EIP padre (de la TSS): %x", stuTSSTablaTareas[uiProcPadre].eip);
	vFnImprimir( "\n fork(): EIP padre (del stack): %x", stuTSSTablaTareas[iPosicion].eip);
	vFnImprimir( "\n fork(): CS padre (del stack): %x", *(unsigned int*)(uiStackUsuario+0x20));
	vFnImprimir( "\n fork(): EFLAGS padre (del stack): %x", *(unsigned int*)(uiStackUsuario+0x24));

	vFnImprimir( "\n fork(): TSS padre: %x", pstuPCB[uiProcPadre].uiIndiceGDT_CS * 8);
	vFnImprimir( "\n fork(): comienzo stack kernel para esta instancia: %x", uiComienzoStackKernel );
	vFnImprimir( "\n fork(): fin stack usuario para esta instancia: %x", uiStackUsuario );
	vFnImprimir( "\n fork(): ESP original: %x", stuTSSTablaTareas[iPosicion].esp );
	vFnImprimir( "\n fork(): EBP original: %x", stuTSSTablaTareas[iPosicion].ebp );

	vFnImprimir( "\n fork(): EDX original: %x", stuTSSTablaTareas[iPosicion].edx );
	vFnImprimir( "\n fork(): ECX original: %x", stuTSSTablaTareas[iPosicion].ecx );
	vFnImprimir( "\n fork(): EBX original: %x", stuTSSTablaTareas[iPosicion].ebx );
	vFnImprimir( "\n fork(): EAX (modificado): %x", stuTSSTablaTareas[iPosicion].eax );
#endif

	/* ajustamos el stack en el hijo para que quede como estaba en el padre antes
	 * de ejecutar el syscall fork() (que provoca que se utilizen 0x28 bytes en el
	 * stack entre lo que pushea el micro y lo que pusheamos nosotros en system_asm.asm)*/
	//stuTSSTablaTareas[iPosicion].esp += 0x28;
    /* PCB */

	iFnCrearPCB( iPosicion, /* PCB asignado al nuevo proceso */
		     0, /* no nos interesa guardar ningun puntero en la PCB */
		     pstuPCB[uiProcPadre].stNombre, /* copiamos el nombre del proceso padre */
		     uiIndiceGDT_CS, /* posicion del descriptor del segmento de codigo para este proceso dentro de la GDT */
		     uiIndiceGDT_DS, /* posicion del descriptor del segmento de datos para este proceso dentro de la GDT */
		     uiIndiceGDT_TSS, /* posicion del descriptor de la TSS dentro de la GDT */
		     iPosicion, /* indice dentro de la tabla de TSS */
		     uiBaseSegmento, /* base del segmento asignado*/
             uiTamSegDatos,     /* LONGITUD del segmento */
             uiTamSegCodigo,    /* Tamanio del codigo */
             uiTamInicializados,/* Tamanio del bloque de datos inicializados */
             uiTamStack,         /* Tamanio del stack */
             uiBaseSS0,
             uiIndiceGDT_SS0
             );

	// el proceso seleccionado tiene un hijo mas!
    ++pstuPCB[uiProcPadre].lNHijos;



    // Agregado 07/10/08:
    pstuPCB[iPosicion].ulParentId = pstuPCB[uiProcPadre].ulId;
    //TODO - Copiar memorias compartidas del padre y todos los demas
    //recursos que el hijo deba heredar


    return iPosicion;
}


/**
 * @brief Reemplaza el proceso indicado y ejecuta desde "cero"
 * @param ulPid PID del proceso a reemplazar
 * @param stArchivo Nombre del archivo binario por el cual reemplazar
 * @returns El PID del proceso modificado (ver nota al final del codigo), o un codigo de error
 */
int iFnReemplazarProceso( unsigned long ulPid, char * stArchivo, unsigned int uiOffset ) {
    unsigned int uiESPOriginal,
             uiStackUsuario,
             uiComienzoStackKernel,
             uiIndiceGDT_DS,
             uiOpt;

    unsigned int uiBaseSegmento;
    unsigned int uiTamSegCodigo;
    unsigned int uiTamSegDatos;
    unsigned int uiTamStack;
    unsigned int uiTamInicializados;
    unsigned int uiTamHeap;

    int iPosicion;
    int iFlags;
    int i;

    stuInfoEjecutable stuInfoExe;



    stuPCB* pPCB;

    /* No deshabilitamos las interrupciones porque esta funcion se invoca desde
     * el syscall execve(), que a su vez es llamado por el handler de la int
     * 0x80, por lo cual al momento de comenzar la atencion de la interrupcion,
     * se deshabilitan las demas
     */

    //Se lee la cabecera del archivo binario
    if( iFnLeerCabeceraEjecutable(stArchivo, &stuInfoExe) == -1) {
        return -1;
    }

    //Vamos a crear descriptores con granularidad 4K, por ello convertimos los
    //datos leidos del binario a granularidad 4K
    uiTamSegCodigo     = REDONDEAR_HACIA_ARRIBA_A_4K(stuInfoExe.uiTamanioTexto);
    uiTamStack         = REDONDEAR_HACIA_ARRIBA_A_4K(stuInfoExe.uiTamanioStack);
    uiTamInicializados = REDONDEAR_HACIA_ARRIBA_A_4K(stuInfoExe.uiTamanioDatosInicializados);
    uiTamHeap          = uiTamHeap;
    uiTamSegDatos      = REDONDEAR_HACIA_ARRIBA_A_4K(uiTamSegCodigo + uiTamStack + uiTamInicializados + uiTamHeap); //Se crea CON heap


    /* No buscamos entradas disponibles en la GDT porque lo unico que tenemos
     * que realizar es reemplazar el codigo del proceso indicado, y resetear el
     * estado, para arrancar este nuevo programa desde "cero"
     */


    /* TENEMOS que alojar el 'nuevo programa' en un nuevo espacio de memoria
     * porque sus segmentos de codigo y datos seran de diferente tamanio que los
     * del original (no siempre, pero casi siempre)
     */

    //Se reserva la memoria para el proceso (se usa un unico segmento para
    //Codigo y Datos)
    uiBaseSegmento = (unsigned int) pvFnReservarParticionUsuario( uiTamSegDatos );
    if( uiBaseSegmento == -1 ) {
        vFnLog("\nNo hay suficiente espacio para la asignacion");
        return -1;
    }


    // En teoria, si pude reservar el espacio en memoria, ya nada puede fallar
    // Ya se puede modificar el proceso original

    pPCB = &pstuPCB[ iFnBuscaPosicionProc(ulPid) ];

    /* Debo cambiar los descriptores de segmento del proceso para que apunten a
     * la nueva ubicacion (y nuevo tamanio) de los segmentos
     */
    uiFnSetearBaseLimiteDescriptor(pPCB->uiIndiceGDT_CS, uiBaseSegmento,
            EN_GRANULARIDAD_4K(uiTamSegCodigo) - 1 );
    uiFnSetearBaseLimiteDescriptor(pPCB->uiIndiceGDT_DS, uiBaseSegmento,
            EN_GRANULARIDAD_4K(uiTamSegDatos) - 1 );

    /* COPIA de CODIGO */

    vFnLog("\niFnReemplazarProceso: Copiando el codigo del proceso \"%s\" de "
            "0x%x a 0x%x, ocupa %d bytes", stuInfoExe.stNombre,
            stuInfoExe.pvPuntoCarga, uiBaseSegmento, stuInfoExe.uiTamanioTexto);
    ucpFnCopiarMemoria(
        (unsigned char*)uiBaseSegmento,
        (unsigned char*)stuInfoExe.pvPuntoCarga,
        stuInfoExe.uiTamanioTexto );
    /* Descriptor de DATOS */
    //uiIndiceGDT_DS = uiFnBuscarEntradaGDTLibre();

    uiOpt = (D_DATA | D_WRITE | D_BIG | D_4KB | D_DPL3);
   // pstuPCB[ulPid].uiIndiceGDT_DS = uiIndiceGDT_DS;
    uiIndiceGDT_DS = pPCB->uiIndiceGDT_DS;// = uiIndiceGDT_DS;


    /* La TSS no se altera; no tiene sentido alterar la TSS del proceso, ya que
     * en el momento que se haga context-switch a otro proceso, lo que este
     * ejecutando en ese momento es lo que va a quedar realmente.
     * Todo lo que pongamos en la TSS aqui se pierde...
     *
     * La estrategia consiste en reemplazar los valores en el stack, tanto la
     * parte que esta en el kernel como la del usuario, de manera que al volver
     * del syscall, no vuelva a la proxima instruccion despues de la invocacion
     * a la int0x80, sino al arranque del proceso nuevo (o sea, tampoco se toca
     * la PCB)
     */

    /* Ahora comenzamos a copiar los valores de los registros, sacandolos de lo
     * que se fue apilando al momento de comenzar el manejo de la syscall
     */
    /* ADVERTENCIA: El comienzo del stack del kernel se calcula en
     * **system_asm.asm**, cualquier cambio ahi (que implique desplazar el
     * stack), debe contemplarse aqui ya que este calculo resultaria invalido
     */




	uiComienzoStackKernel = uiKernelStack - 0x08 - 0x100 * ((pstuPCB[iFnBuscaPosicionProc(ulPid)].uiIndiceGDT_TSS * 8)-0x20);

	uiStackUsuario = *(int*)(uiComienzoStackKernel - 0x08)  + pstuPCB[iFnBuscaPosicionProc(ulPid)].uiBaseSs0;

   //     vFnImprimir("\nStack Kernel: 0x%x",uiComienzoStackKernel);
	//vFnImprimir("\nStack usuario: 0x%x",uiStackUsuario); // Situado en ESP0 + SS0


	////////////////////////////////////////////////////


	*(unsigned int*)(uiComienzoStackKernel-0x0c) = POS_STACK - uiOffset; // EBP
	*(unsigned int*)(uiComienzoStackKernel-0x10) = 0x0; // EDX
	*(unsigned int*)(uiComienzoStackKernel-0x14) = 0x0; // ECX
 	*(unsigned int*)(uiComienzoStackKernel-0x18) = 0x0; // EBX


	/* calculamos la posicion del fin stack del usuario, tomando la base del segmento
	 * del proceso (de su PCB) y el valor de ESP (apilado en el stack del kernel) */

	  *(unsigned int*)(uiStackUsuario-0x04) = 0x3202L; // EFLAGS
	  *(unsigned int*)(uiStackUsuario+0x00) = 0x0; // ESI
	  *(unsigned int*)(uiStackUsuario+0x04) = 0x0; // EDI
	  *(unsigned int*)(uiStackUsuario+0x1C) = 0x0; // EIP
	  *(unsigned int*)(uiStackUsuario+0x28) = POS_STACK  - uiOffset; // SS (queremos que sea el inicial)


	////////////////////////////////////////////////////



    /* PCB */

    /* TODO: Actualizar lo que falte actualizar en el PCB
     * Solo hay que cambiar en el PCB lo que haya que cambiar; ver: man execve
     */
    pPCB->vFnFuncion    = 0x00;
    pPCB->uiTamProc     = uiTamSegDatos;
    pPCB->uiDirBase     = uiBaseSegmento;
    pPCB->uiLimite      = uiTamSegDatos;
    pPCB->uiTamanioTexto                = uiTamSegCodigo;
    pPCB->uiTamanioDatosInicializados   = uiTamInicializados;
    pPCB->uiTamanioStack                = uiTamStack;
    //TODO: Revisar
    /* La memoria 'extra' asignada al segmento de datos se puede reutilizar
     * cuando crece el stack, en cambio lo que se asigna de mas al segmento de
     * codigo no se reutiliza.
     */
    pPCB->uiTamanioOverhead =
                                    uiTamSegCodigo - stuInfoExe.uiTamanioTexto;
    pPCB->uiEsperaTeclado = 0;

    ucpFnCopiarMemoria((unsigned char*)&pPCB->stuCabeceraELF,(unsigned char*)ucLecturaTemporalELF, TAM_ELF);
	ucpFnCopiarMemoria((unsigned char*)&pPCB->stSHeaderData,(unsigned char*)ucLecturaTemporalSHeaederELF, TAM_SHT_ELF);
	
    // TODO: Liberar todos los recursos que haya que liberar

    //Desadjuntamos todas las memorias compartidas
    iFnShmDtAllProc( ulPid );

    // LISTO!
    /* En realidad, si execve no falla, no retorna nada (el nuevo programa ni
     * siquiera sabe que se llamo a execve). Esta devolucion se pierde por los
     * manejos del stack anteriores, pero es necesaria para que compile (podemos
     * devolver cualquier cosa)
     */
    pPCB->uiIndiceGDT_DS = uiIndiceGDT_DS;

    return (int) ulPid;
}


/**
 * @brief Elimina el proceso indicado (PCB, segmentos y entradas en la GDT)
 * @param Posicion del proceso dentro de la tabla de PCBs
 * @returns La posicion dentro de la tabla de PCBs del proceso eliminado
 */
int iFnEliminarProceso( unsigned int uiProceso ){
    int iPCBPadre;

    vFnLog( "\niFnEliminarProceso(): Liberando el segmento base %x (%d bytes)",
             pstuPCB[ uiProceso ].uiDirBase, pstuPCB[ uiProceso ].uiLimite);

	// Grupo N° 1 - Eliminamos el proceso y desasignamos paginas
	/* if (usMemPaginadaHabilitada == MODOPAGINADO)
		iFnDesasignarMemoriaProceso(pstuPCB[uiProceso].ulId);
	*/
    vFnLiberarParticion( (void *)pstuPCB[ uiProceso ].uiDirBase);
    vFnLiberarParticion( (void *)pstuPCB[ uiProceso ].uiBaseSs0);

    vFnLog( "\n iFnEliminarProceso(): Liberando entradas %d, %d y %d de la GDT, del pid: %d",
             pstuPCB[ uiProceso ].uiIndiceGDT_CS,
             pstuPCB[ uiProceso ].uiIndiceGDT_DS,
             pstuPCB[ uiProceso ].uiIndiceGDT_TSS,
             uiProceso );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_CS,  0 );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_DS,  0 );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_TSS, 0 );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_SS0, 0 );

    /* Liberamos las 3 entradas de la GDT reservadas para el descriptor
     * de segmento de codigo, datos y TSS: */
    vFnLog( "\n iFnEliminarProceso(): Liberando entradas %d, %d y %d de la GDT, del pid: %d",
             pstuPCB[ uiProceso ].uiIndiceGDT_CS,
             pstuPCB[ uiProceso ].uiIndiceGDT_DS,
             pstuPCB[ uiProceso ].uiIndiceGDT_TSS,
             uiProceso );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_CS,  0 );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_DS,  0 );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_TSS, 0 );
    mapa_gdt_set( pstuPCB[ uiProceso ].uiIndiceGDT_SS0, 0 );

    // Despertamos al padre, si es que lo estaba esperando
    iPCBPadre = iFnBuscaPosicionProc( pstuPCB[ uiProceso ].ulParentId );
    if (pstuPCB[iPCBPadre].iEstado == PROC_ESPERANDO) {
        pstuPCB[iPCBPadre].iEstado = PROC_LISTO;
    }

    //Desadjuntamos todas las memorias compartidas
    iFnShmDtAllProc( pstuPCB[ uiProceso ].ulId );

    /* Liberamos la PCB que utilizaba (y por consiguiente, la entrada en la
     * tabla de TSS */
    vFnLog( "\n iFnEliminarProceso(): Eliminando PCB %d (PID %d)", uiProceso,
             pstuPCB[ uiProceso ].ulId );
    pstuPCB[ uiProceso ].iEstado = PROC_ELIMINADO;


    return uiProceso;
}

/**
\brief Abre un archivo ejecutable y recupera la informacion de su cabecera
\param stArchivo Nombre del archivo ejecutable
\param pstuInfo Puntero a una estructura stuInfoEjecutable, donde se almacenan los datos leidos
\returns 0 si concluye con exito, -1 si hay error
*/
int iFnLeerCabeceraEjecutable(char* stArchivo, stuInfoEjecutable* pstuInfo) {

    int i;
    stEntradaLS *pstEntLs;
    stDirectorio *pstDirBusqueda;
    stuHeaderELF *stELF;
	
    if (iFnObtenerDirectorio("/mnt/usr", &pstDirBusqueda) < 0) {
        vFnImprimir("\nSodium Dice: Error! Directorio /mnt/usr no existe");
        return -1;
    }
    if( iFnBuscarArchivo(pstDirBusqueda, stArchivo, &pstEntLs) < 0) {
        vFnImprimir("\nSodium Dice: Error! El archivo %s no existe", stArchivo);
        return -1;
    }

    pstuInfo->pvPuntoCarga = (void*)DIR_LINEAL(pstEntLs->wSeg,pstEntLs->wOff);

    /* Estoy en el punto inicial frente a la cabecera ELF. La voy a leer. */

    ucpFnCopiarMemoria((unsigned char*)ucLecturaTemporalELF,(unsigned char*)pstuInfo->pvPuntoCarga, TAM_ELF);
	
    stELF = &ucLecturaTemporalELF;
	 
	/* Copia a memoria temporal las tablas de Program Header y Section Header */
	ucpFnCopiarMemoria((unsigned char*)ucLecturaTemporalPHeaederELF,(unsigned char*)(pstuInfo->pvPuntoCarga + stELF->elfHeaderData.phoff), TAM_PHT_ELF);
	ucpFnCopiarMemoria((unsigned char*)ucLecturaTemporalSHeaederELF,(unsigned char*)(pstuInfo->pvPuntoCarga + stELF->elfHeaderData.sphoff), TAM_SHT_ELF);
	
	ucpFnCopiarMemoria((unsigned char*)&(stELF->PHeaderData),(unsigned char*)ucLecturaTemporalPHeaederELF, TAM_PHT_ELF);
	
	/* Ahora le sumo el tamaño de la Cabecera ELF, por defecto 0x80 para cargar el binario puro sin cabecera */
    pstuInfo->pvPuntoCarga = pstuInfo->pvPuntoCarga + TAM_ELF;

    pstuInfo->uiTamanioTexto  = stELF->PHeaderData[0].memsz;
    pstuInfo->uiTamanioDatosInicializados = 0;
    pstuInfo->uiTamanioStack  = STACK_SIZE;

    for (i = 0; i < 11; i++) {
        pstuInfo->stNombre[i] = pstEntLs->strNombreArchivo[i];
    }
    pstuInfo->stNombre[i] = '\0';

    return 0;
}

/**
\brief Verifica si existe el archivo ejecutable sin leer su cabecera
\param stArchivo Nombre del archivo ejecutable
\returns 1 si encuentra, -1 si no
*/
int iFnChequearEjecutable(char* stArchivo) {

    /**
     * \note Hoy en dia los ejecutables que acepta Sodium son binarios planos,
     * por lo que no hay cabecera a leer; se toman los valores desde el RamFS.
     * Cuando Sodium tenga un mejor soporte de FS e interprete archivos ELF, se
     * deberia modificar esta funcion para que los interprete.
     */

    int i;
    stEntradaLS *pstEntLs;
    stDirectorio *pstDirBusqueda;


    if (iFnObtenerDirectorio("/mnt/usr", &pstDirBusqueda) < 0) {
        vFnImprimir("\nSodium Dice: Error! Directorio /mnt/usr no existe");
        return 0;
    }
    if( iFnBuscarArchivo(pstDirBusqueda, stArchivo, &pstEntLs) < 0) {
        return 0;
    }


    return 1;
}

/**
 * @brief Todavìa no codificada.
 */
int iFnClonarProceso()
{
    return 0;
}


/**
 * @brief Instancia un proceso de programa de usuario
 * @returns La posicion dentro de la Tabla de PCBs
 */
int iFnInstanciarProceso(char *stProceso, int iPrivilegio, int iArgumento)
{
    return iFnCrearProceso(stProceso,iPrivilegio,iArgumento);
}



